/**
 *
 */
package com.cmcc.pcap.packet.tcp;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.cmcc.pcap.packet.util.CommonUtils;
import org.pcap4j.packet.IpPacket;
import org.pcap4j.packet.TcpPacket;

import java.sql.Timestamp;
import java.util.ArrayList;

/**
 * @Date 2018年1月17日 上午8:35:58
 * @author Amish
 * 
 * (1)包装jpcap.packet.TCPPacket的类型
 * (2)提供辅助信息，如Packet接收时间，是否是tcp 三次握手的第一条消息等
 */
public class TcpPacketDecorator {
	public static Logger logger = LogManager.getLogger(TcpPacketDecorator.class);
	
	public boolean isFirst = false;//表示是TCP连接的第一个SYN消息
	public long receiveTime;//非抓包时间
	public TcpProtocol protocol;//应用层协议，默认为空
	
	public IpPacket ipPacket;
	public TcpPacket tcpPacket;
	public Timestamp timestamp;//包时间戳
	public long lastPacketTime;//最后一个包的时间 TODO
	
	public long relativeSequenceNumber;
	public long relativeACKNumber;
	
	public boolean isOutOfOrder = false;//true表示是一个乱序的包
	
    public boolean isReassemble = false;//true表示是一个分段重组后的包
    public ArrayList<TcpPacketDecorator> segmentsAl;
    
    public boolean isDuplicateAckMerge = false;//true表示是一个Duplicate ACK merge后的包
    public ArrayList<TcpPacketDecorator> duplicateAckMergeAl;
    
    public boolean isRetransmissionMerge = false;//true表示是一个Retransmission merge后的包
    public ArrayList<TcpPacketDecorator> retransmissionMergeAl;
	
	public TcpPacketDecorator(boolean isFirst){
		this.receiveTime = System.currentTimeMillis();//初始化时获取当前时间(非抓包时间)
		this.isFirst = isFirst;
	}
	
	@Deprecated
	public byte[] getData(){
		
		if(!isReassemble){
			return ipPacket.get(TcpPacket.class).getRawData();
		}else {
			byte[] reassembleData = this.ipPacket.get(TcpPacket.class).getRawData();
			for(int i=0;i<segmentsAl.size();i++){
				reassembleData = CommonUtils.jointBytes(reassembleData, segmentsAl.get(i).ipPacket.get(TcpPacket.class).getRawData());
			}
			return reassembleData;
		}
	}
	
	/**
	 * lvpin20180719
	 * 测试验证后用该方法替代getData()方法，对于上万个分段的包，合并所有分段会十分耗费内存及性能
	 * 该方法支持解析HTTP时不必合并所有segments，可以通过参数选择要合并的分段数
	 * 当参数reSegments等于0时，合并所有分段
	 * 当参数reSegments大于0，小于总分段数时，合并前segments个分段
	 * 但参数reSegments大于总分段数时，合并所有分段
	 * @param reSegments
	 * @return
	 */
	public byte[] getData(int reSegments){

		if(!isReassemble){
			return ipPacket.get(TcpPacket.class).getRawData();
		}else {
			int sumSegments = segmentsAl.size();//实际的分段总数
			int num = (0 < reSegments && reSegments < sumSegments) ? reSegments : sumSegments;
			byte[] reassembleData = this.ipPacket.get(TcpPacket.class).getRawData();
			for(int i=0;i<num;i++){
				reassembleData = CommonUtils.jointBytes(reassembleData, segmentsAl.get(i).ipPacket.get(TcpPacket.class).getRawData());
			}
			return reassembleData;
		}
	}
	

}
